package element;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import springiesSimulator.Assembly;
import util.Vector;


/**
 * @author Robert C. Duvall modified by Mark Govea and Mark Hoffman
 */
public class Spring implements RenderableObject {
    protected static final double REST_THRESHOLD = 0.001;
    private Mass myStart;
    private Mass myEnd;
    private double myLength;
    private double myK;

    /**
     * @param start the first mass attached to a spring
     * @param end the second (final) mass attached to a spring
     * @param length distance between the masses
     * @param kVal the spring constant in Hooke's law
     */
    public Spring (Mass start, Mass end, double length, double kVal) {
        setMyStart(start);
        setMyEnd(end);
        setMyLength(length);
        setMyK(kVal);
    }

    @Override
    public void paint (Graphics2D pen) {
        setPaintColor(pen);
        // the following line is just pen.drawLine(startX, startY, endX, endY);
        pen.drawLine((int) getMyStart().getCenter().getX(),
                (int) getMyStart().getCenter().getY(),
                (int) getMyEnd().getCenter().getX(),
                (int) getMyEnd().getCenter().getY());
    }

    /**
     * @param pen for selecting which color to use
     */
    public void setPaintColor (Graphics2D pen) {
        double len = getCurrentCompression();

        if (Math.abs(len) < REST_THRESHOLD) {
            pen.setColor(Color.BLACK);
        }
        else if (len > 0.0) {
            pen.setColor(Color.BLUE);
        }
        else {
            pen.setColor(Color.RED);
        }
    }

    @Override
    public void update (Assembly canvas, double dt) {
        // apply hooke's law to each attached mass
        Vector f = getForce();
        getMyStart().applyForce(new Vector(f));
        f.negate();
        getMyEnd().applyForce(f);
    }

    /**
     * @return current length of spring based on the location of the two anchor
     *         masses
     */
    public double getCurrentLength () {
        double[] dxdy = getWidthAndHeight();

        return Vector.distanceBetween(dxdy[0], dxdy[1]);
    }

    /**
     * @return width (result[0]) and height (result[1])
     */
    public double[] getWidthAndHeight () {
        double[] result = new double[2];

        Point2D start = getMyStart().getCenter();
        Point2D end = getMyEnd().getCenter();

        result[0] = start.getX() - end.getX();
        result[1] = start.getY() - end.getY();

        return result;
    }

    /**
     * @return the force exerted on the first mass based on current compression
     */
    public Vector getForce () {
        double[] dxdy = getWidthAndHeight();

        Vector f = new Vector(Vector.angleBetween(dxdy[0], dxdy[1]), getMyK()
                * getCurrentCompression());

        return f;
    }

    /**
     * @return current compression of spring relative to rest length
     */
    public double getCurrentCompression () {
        return getRestLength() - getCurrentLength();
    }

    /**
     * @return the spring's rest length
     */
    public double getRestLength () {
        return getMyLength();
    }

    // Getters and Setters:

    /**
     * @return first connected mass
     */
    public Mass getMyStart () {
        return myStart;
    }

    /**
     * @param myStart sets the first connected mass
     */
    protected void setMyStart (Mass start) {
        myStart = start;
    }

    /**
     * @return second connected mass
     */
    public Mass getMyEnd () {
        return myEnd;
    }

    /**
     * @param end sets the second connected mass
     */
    protected void setMyEnd (Mass end) {
        myEnd = end;
    }

    /**
     * @return default rest length of spring
     */
    public double getMyLength () {
        return myLength;
    }

    /**
     * @param length sets default spring rest length
     */
    protected void setMyLength (double length) {
        myLength = length;
    }

    /**
     * @return spring constant
     */
    public double getMyK () {
        return myK;
    }

    /**
     * @param kVal sets spring constant
     */
    protected void setMyK (double kVal) {
        myK = kVal;
    }
}
